package com.tinyengine.it.service.generator;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.map.MapBuilder;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.tinyengine.it.common.utils.*;
import com.tinyengine.it.controller.generator.JavaCodeFormatter;
import com.tinyengine.it.mapper.ApiGatewayServiceMapper;
import com.tinyengine.it.mapper.DbLogicalInfoMapper;
import com.tinyengine.it.model.entity.*;
import com.tinyengine.it.model.vo.VoLogTableFiledTemplateVo;
import com.tinyengine.it.service.db.DbLogicalFieldsInfoService;
import com.tinyengine.it.service.db.DbLogicalInfoService;
import com.tinyengine.it.service.db.DbTableFieldsInfoService;
import com.tinyengine.it.service.db.DbTableInfoService;
import com.tinyengine.it.tool.TemplateClassProcessor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Description: 构建Service、mapper、xml 等代码
 * @Author: Ping.Liu
 * @Date: 2025-03-17 - 16:46
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class GenServiceMapperService {
    private final DbTableInfoService dbTableInfoService;
    private final DbTableFieldsInfoService dbTableFieldsInfoService;
    private final DbLogicalInfoService dbLogicalInfoService;
    private final DbLogicalFieldsInfoService dbLogicalFieldsInfoService;
    private final ApiGatewayServiceMapper apiGatewayServiceMapper;
    private final DbLogicalInfoMapper dbLogicalInfoMapper;

    /**
     * @Description: 构建Vo实体
     * @Author: Ping.Liu
     * @Date: 2025-03-18 - 9:32
     */
    public void GenServiceMapperMain(Integer appId) throws IOException {
        //构建Vo
        BuildVo(appId);
        //构建实体
        BuildEntity(appId);
        //构建mapper  xml
        BuildMapperXml(appId);
        //构建service
        BuildService(appId);
        //格式化代码
        formaterCode(appId);
    }

    public void formaterCode(Integer appId) {
        List<ApiGatewayService> gatewayServices = apiGatewayServiceMapper.selectList(Wrappers.lambdaQuery());
        List<String> path = new ArrayList<>();
        String baseDir = GenDir.getSpringOptTargetPathBasedOnOS(appId) + FileUtil.FILE_SEPARATOR;
        for (ApiGatewayService service : gatewayServices) {
            String serciePath = baseDir + GenDir.bladeService + service.getServiceEn() + FileUtil.FILE_SEPARATOR;
            String serciePathApi = baseDir + GenDir.bladeServiceApi + service.getServiceEn() + GenDir.apiTag + FileUtil.FILE_SEPARATOR;
            path.add(serciePath);
            path.add(serciePathApi);
        }
        path.parallelStream().forEach(e -> JavaCodeFormatter.formatJavaFiles(e));
    }

    private void BuildService(Integer appId) {
        List<DbTableInfo> tableInfos = dbTableInfoService.list(new LambdaQueryWrapper<DbTableInfo>().eq(DbTableInfo::getAppId, appId));
        MapBuilder<String, Object> setviceModel = MapUtil.builder();
        MapBuilder<String, Object> setviceImplModel = MapUtil.builder();

        for (DbTableInfo tableInfo : tableInfos) {

            //mapper java部分
            List<DbTableFieldsInfo> fieldsInfos = dbTableFieldsInfoService.list(Wrappers.<DbTableFieldsInfo>lambdaQuery().eq(DbTableFieldsInfo::getTableId, tableInfo.getId()));
            if (fieldsInfos.isEmpty()) {
                log.info("BuildService该表中么有配置字段信息：{}", tableInfo.getNameCh());
                continue;
            }
            ApiGatewayService service = apiGatewayServiceMapper.selectById(tableInfo.getServiceId());
            String serviceName = service.getServiceEn();
            setviceModel.put("package", serviceName);
            setviceModel.put("tableNameCh", tableInfo.getNameCh());
            String entityName = StrUtil.upperFirst(StrUtil.toCamelCase(tableInfo.getName()));
            String className = entityName + "Service";
            setviceModel.put("tableName", className);
            setviceModel.put("entityName", entityName);
            String packageDir = String.format(GenDir.packageDir, serviceName) + FileUtil.FILE_SEPARATOR + "entity" + FileUtil.FILE_SEPARATOR + entityName;
            String packagePath = GenUtils.slashTurningPoint(packageDir);
            setviceModel.put("imports", ListUtil.toList(packagePath));
            String servicePath = GenDir.getSpringOptTargetPathBasedOnOS(appId) + FileUtil.FILE_SEPARATOR + GenDir.bladeService + serviceName + FileUtil.FILE_SEPARATOR + GenDir.srcMainDir + String.format(GenDir.packageDir, serviceName) + "service" + FileUtil.FILE_SEPARATOR;
            String servicePathInter = servicePath + className;
            File serviceFile = FileUtil.touch(servicePathInter + ".java");
            TemplateClassProcessor.processTemplate("service.java.ftl", setviceModel.build(), serviceFile);

            String impl = "Impl";
            String servicePathImpl = servicePath + impl.toLowerCase();
            FileUtil.mkdir(servicePathImpl);
            setviceImplModel.put("package", serviceName);
            setviceImplModel.put("tableNameCh", tableInfo.getNameCh());
            setviceImplModel.put("className", className + impl);
            setviceImplModel.put("tableName", entityName);
            setviceImplModel.put("tableService", className);
            String mapperPath = entityName + "Mapper";
            setviceImplModel.put("tableNameMapper", mapperPath);
            setviceImplModel.put("imports", ListUtil.toList(
                    GenUtils.slashTurningPoint(String.format(GenDir.packageDir, serviceName) + "mapper" + FileUtil.FILE_SEPARATOR + mapperPath),
                    GenUtils.slashTurningPoint(String.format(GenDir.packageDir, serviceName) + "entity" + FileUtil.FILE_SEPARATOR + entityName),
                    GenUtils.slashTurningPoint(String.format(GenDir.packageDir, serviceName) + "service" + FileUtil.FILE_SEPARATOR + className)
            ));
            String serviceImplPath = servicePathImpl + FileUtil.FILE_SEPARATOR + className + impl;
            File serviceImplFile = FileUtil.touch(serviceImplPath + ".java");
            TemplateClassProcessor.processTemplate("serviceImpl.java.ftl", setviceImplModel.build(), serviceImplFile);
        }
    }

    private void BuildMapperXml(Integer appId) {
        List<DbTableInfo> tableInfos = dbTableInfoService.list(new LambdaQueryWrapper<DbTableInfo>().eq(DbTableInfo::getAppId, appId));
        MapBuilder<String, Object> mapperModel = MapUtil.builder();
        MapBuilder<String, Object> xmlModel = MapUtil.builder();

        for (DbTableInfo tableInfo : tableInfos) {

            //mapper java部分
            List<DbTableFieldsInfo> fieldsInfos = dbTableFieldsInfoService.list(Wrappers.<DbTableFieldsInfo>lambdaQuery().eq(DbTableFieldsInfo::getTableId, tableInfo.getId()));
            if (fieldsInfos.isEmpty()) {
                log.info("BuildMapper该表中么有配置字段信息：{}", tableInfo.getNameCh());
                continue;
            }
            ApiGatewayService service = apiGatewayServiceMapper.selectById(tableInfo.getServiceId());
            String serviceName = service.getServiceEn();
            mapperModel.put("package", serviceName);
            mapperModel.put("tableNameCh", tableInfo.getNameCh());
            String entityName = StrUtil.upperFirst(StrUtil.toCamelCase(tableInfo.getName()));
            String className = entityName + "Mapper";
            mapperModel.put("tableName", className);
            mapperModel.put("entityName", entityName);
            String packageDir = String.format(GenDir.packageDir, serviceName) + FileUtil.FILE_SEPARATOR + "entity" + FileUtil.FILE_SEPARATOR + entityName;
            String packagePath = GenUtils.slashTurningPoint(packageDir);
            mapperModel.put("imports", ListUtil.toList(packagePath));

            String mapperPath = GenDir.getSpringOptTargetPathBasedOnOS(appId) + FileUtil.FILE_SEPARATOR + GenDir.bladeService + serviceName + FileUtil.FILE_SEPARATOR + GenDir.srcMainDir + String.format(GenDir.packageDir, serviceName) + "mapper" + FileUtil.FILE_SEPARATOR + className;
            File mapperFile = FileUtil.touch(mapperPath + ".java");
            TemplateClassProcessor.processTemplate("mapper.java.ftl", mapperModel.build(), mapperFile);

            //mapper xml部分
            String mapperPackageDir = String.format(GenDir.packageDir, serviceName) + "mapper" + FileUtil.FILE_SEPARATOR + className;
            xmlModel.put("mapperPackage", GenUtils.slashTurningPoint(mapperPackageDir));
            File mapperXmlFile = FileUtil.touch(mapperPath + ".xml");
            TemplateClassProcessor.processTemplate("mapper.xml.ftl", xmlModel.build(), mapperXmlFile);
        }
    }

    private void BuildEntity(Integer appId) {
        List<DbTableInfo> tableInfos = dbTableInfoService.list(new LambdaQueryWrapper<DbTableInfo>().eq(DbTableInfo::getAppId, appId));

        List<DbTableFieldsInfo> fieldsInfos = dbTableFieldsInfoService.list(new LambdaQueryWrapper<DbTableFieldsInfo>().in(
                DbTableFieldsInfo::getTableId, tableInfos.stream().map(m -> m.getId()).collect(Collectors.toList())
        ));

        for (DbTableInfo tableInfo : tableInfos) {
            MapBuilder<String, Object> dataModel = MapUtil.builder();

            ApiGatewayService service = apiGatewayServiceMapper.selectById(tableInfo.getServiceId());
            String serviceName = service.getServiceEn();
            dataModel.put("package", serviceName);

            //获取字段信息
            List<DbTableFieldsInfo> fieldsInfoList = fieldsInfos.stream().filter(f -> ObjUtil.isNotNull(f.getTableId()) && f.getTableId().equals(tableInfo.getId())).collect(Collectors.toList());
            if (CollUtil.isEmpty(fieldsInfoList)) {
                log.info("genEntity该逻辑表中不存在字段信息：{}--{}", tableInfo.getName(), tableInfo.getNameCh());
                continue;
            }

            List<VoLogTableFiledTemplateVo> fields = new ArrayList<>();
            Set<String> imports = new HashSet<>();

            List<Field> baseField = CollUtil.toList(BaseEntity.class.getDeclaredFields());
            List<String> baseFields = baseField.stream().map(m -> m.getName()).collect(Collectors.toList());
            fieldsInfoList.forEach(f -> {
                        if (baseFields.contains(StrUtil.toCamelCase(f.getFieldCode()))) {
                            return;
                        }

                        if (StrUtil.isBlank(f.getDataType())) {
                            log.info("genEntity表:{}中，{}--的数据类型为空", tableInfo.getNameCh(), f.getFieldCode());
                            return;
                        }
                        fields.add(new VoLogTableFiledTemplateVo()
                                .setComment(f.getNameCh())
                                .setPropertyName(StrUtil.toCamelCase(f.getFieldCode()))
                                .setPropertyType(DbDataTypeMap.getJavaType(f.getDataType())));
                        imports.add(DbDataTypeMap.getWrapperType(f.getDataType()));
                    }
            );

            String fileName = StrUtil.upperFirst(StrUtil.toCamelCase(tableInfo.getName()));
            dataModel.put("table", MapUtil.builder("fields", fields).build());
            dataModel.put("comment", tableInfo.getNameCh());
            dataModel.put("entityName", fileName);
            dataModel.put("tableName", tableInfo.getName());
            dataModel.put("imports", imports);

            String entityPath = GenDir.getSpringOptTargetPathBasedOnOS(appId) + FileUtil.FILE_SEPARATOR + GenDir.bladeServiceApi + serviceName + GenDir.apiTag + FileUtil.FILE_SEPARATOR
                    + GenDir.srcMainDir + String.format(GenDir.packageDir, serviceName) + "entity" + FileUtil.FILE_SEPARATOR + fileName + ".java";
            File entityFile = FileUtil.touch(entityPath);

            TemplateClassProcessor.processTemplate("entity.java.ftl", dataModel.build(), entityFile);
        }
    }

    private void BuildVo(Integer appId) {
        List<DbLogicalInfo> logTables = dbLogicalInfoService.list(new LambdaQueryWrapper<DbLogicalInfo>().eq(DbLogicalInfo::getAppId, appId));

        List<DbLogicalFieldsInfo> fieldsInfos = dbLogicalFieldsInfoService.list(new LambdaQueryWrapper<DbLogicalFieldsInfo>().in(
                DbLogicalFieldsInfo::getTableId, logTables.stream().map(m -> m.getId()).collect(Collectors.toList())
        ));

        for (DbLogicalInfo logTable : logTables) {
            MapBuilder<String, Object> dataModel = MapUtil.builder();

            ApiGatewayService service = apiGatewayServiceMapper.selectById(logTable.getServiceId());

            String serviceName = service.getServiceEn();
            dataModel.put("package", serviceName);

            //获取字段信息
            List<DbLogicalFieldsInfo> fieldsInfoList = fieldsInfos.stream().filter(f -> ObjUtil.isNotNull(f.getTableId()) && f.getTableId().equals(logTable.getId())).collect(Collectors.toList());
            //if (CollUtil.isEmpty(fieldsInfoList)) {
            //    log.info("该逻辑表中不存在字段信息：{}--{}", logTable.getCode(), logTable.getName());
            //    continue;
            //}

            List<VoLogTableFiledTemplateVo> fields = new ArrayList<>();
            Set<String> imports = new HashSet<>();
            for (DbLogicalFieldsInfo field : fieldsInfoList) {
                if (StrUtil.isBlank(field.getDataType())) {
                    log.info("表:{}中，{}--的数据类型为空", logTable.getName(), field.getName());
                    continue;
                }
                JavaDataType dataType = JavaDataType.fromValue(field.getDataType().toUpperCase());
                switch (dataType) {
                    case List_Object:
                        imports.add("java.util.List");
                        //ApiDirectory serviceInfo = apiDirectoryMapper.selectById(logTable.getServiceId());
                        DbLogicalInfo logicalInfoVo = dbLogicalInfoMapper.selectById(field.getShipVoId());
                        String voName = StrUtil.upperFirst(StrUtil.toCamelCase(logicalInfoVo.getCode()) + "Vo");
                        //当实体在同一包路径下，可以不导入
                        //imports.add(GenUtils.slashTurningPoint(String.format(GenDir.packageDir, serviceInfo.getCode()) + "vo" + FileUtil.FILE_SEPARATOR + voName));

                        fields.add(new VoLogTableFiledTemplateVo()
                                .setComment(field.getName())
                                .setPropertyName(StrUtil.toCamelCase(field.getCode()))
                                .setPropertyType("List<" + voName + ">"));
                        break;
                    case Object:
                        //字段为 object
                        //ApiDirectory serviceInfo = apiDirectoryMapper.selectById(logTable.getServiceId());
                        DbLogicalInfo logicalInfoVoSub = dbLogicalInfoMapper.selectById(field.getShipVoId());
                        String voType = StrUtil.upperFirst(StrUtil.toCamelCase(logicalInfoVoSub.getCode()) + "Vo");
                        //imports.add(GenUtils.slashTurningPoint(String.format(GenDir.packageDir, serviceInfo.getCode()) + "vo" + FileUtil.FILE_SEPARATOR + voName));

                        fields.add(new VoLogTableFiledTemplateVo()
                                .setComment(field.getName())
                                .setPropertyName(StrUtil.toCamelCase(field.getCode()))
                                .setPropertyType(voType));
                        break;
                    case File:
                        //该文件类型 @RequestBody 不能解析二进制数据  ； vo将被放置在 RequestBody
                        break;
                    default:
                        fields.add(new VoLogTableFiledTemplateVo()
                                .setComment(field.getName())
                                .setPropertyName(StrUtil.toCamelCase(field.getCode()))
                                .setPropertyType(DbDataTypeMap.getJavaType(field.getDataType())));
                        imports.add(DbDataTypeMap.getWrapperType(field.getDataType()));

                }
                if (JavaDataType.Blob.getValue().equals(field.getDataType().toUpperCase())) {
                    imports.add("org.apache.ibatis.type.BlobTypeHandler");
                    imports.add("com.baomidou.mybatisplus.annotation.TableField");
                    dataModel.put("isBlobTypeField", StrUtil.toCamelCase(field.getCode()));
                }
            }

            String fileName = StrUtil.upperFirst(StrUtil.toCamelCase(logTable.getCode()) + "Vo");
            dataModel.put("table", MapUtil.builder("fields", fields).build());
            dataModel.put("comment", logTable.getName());
            dataModel.put("entityName", fileName);
            dataModel.put("imports", imports);

            String voPath = GenDir.getSpringOptTargetPathBasedOnOS(appId) + FileUtil.FILE_SEPARATOR + GenDir.bladeServiceApi + serviceName + GenDir.apiTag + FileUtil.FILE_SEPARATOR
                    + GenDir.srcMainDir + String.format(GenDir.packageDir, serviceName) + "vo" + FileUtil.FILE_SEPARATOR + fileName + ".java";
            File voFile = FileUtil.touch(voPath);
            TemplateClassProcessor.processTemplate("vo.java.ftl", dataModel.build(), voFile);
        }
    }
}
